package org.keqinglee.study.concurrent.lock;

import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

/**
 * @description: org.keqinglee.study.concurrent.lock
 * @author: liqing
 * @date: 2022/9/1
 */
public class SynchronizedTest {

    private static final ThreadFactory THREAD_FACTORY = new ThreadFactory() {
        private final AtomicLong atomicLong = new AtomicLong(0);

        @Override
        public Thread newThread(Runnable r) {
            String name = String.format("【测试线程-%d】", atomicLong.incrementAndGet());
            return new Thread(r, name);
        }
    };

    public static void main(String[] args) {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<>(), THREAD_FACTORY);

        SynchronizedTest sync = new SynchronizedTest();
        for (int i = 0; i < 3; i++) {
            //锁class
            executor.execute(SynchronizedTest::lockStaticMethod);
//            executor.execute(sync::lockClass);
            //锁对象
//            executor.execute(sync::lockMethod);
//            executor.execute(sync::lockObject);
//            executor.execute(() -> {
//                synchronized (sync) {
//                    sync.run();
//                }
//            });
//            executor.execute(sync::lockBoolean3);
//            executor.execute(sync::lockInteger);
        }

        try {
            TimeUnit.SECONDS.sleep(6);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("结束运行");
        executor.shutdownNow();
    }

    /**
     * 修饰普通方法 -> 锁当前对象
     */
    public synchronized void lockMethod() {
        this.run();
    }

    /**
     * 修饰静态方法 -> 锁当前class
     */
    public synchronized static void lockStaticMethod() {
        SynchronizedTest sync = new SynchronizedTest();
        sync.run();
    }

    /**
     * 修饰代码块 -> 锁指定class
     */
    public void lockClass() {
        synchronized (SynchronizedTest.class) {
            this.run();
        }
    }

    /**
     * 修饰代码块 -> 锁指定对象
     */
    public void lockObject() {
        synchronized (this) {
            this.run();
        }
    }

    /**
     * 锁字符串常见问题
     */
    public void lockString() {
        //String类型不可变，如果加载同步关键字中生效吗，为什么？
        //new String是个新对象，只是字符串的值在常量池中，锁在了不同对象上，所以不生效
        String str = new String("同步测试");
        synchronized (str) {
            this.run();
        }

        //如果使用intern方法 返回的是常量池中的对象，是个唯一的，所以锁能生效
        synchronized (str.intern()) {
            this.run();
        }
    }

    private static Boolean flag = false;
    private static Boolean flag2 = Boolean.FALSE;

    /**
     * 锁boolean类型常见问题
     * 1.死锁
     */
    public void lockBoolean1() {
        synchronized (flag) {
            if (flag == false) {
                this.run();
                flag = true;
            }
        }
    }

    /**
     * 2.以为锁的是同一个对象，结果锁未生效
     */
    public void lockBoolean2() {
        synchronized (flag) {
            flag = !flag;
            this.run();
        }
    }

    /**
     * 3.以为锁的是不同对象，其实是一个对象
     */
    public void lockBoolean3() {
        lockBoolean31();
        lockBoolean32();
    }

    public void lockBoolean31() {
        synchronized (flag) {
            this.run();
        }
    }

    public void lockBoolean32() {
        synchronized (flag2) {
            this.run();
        }
    }

    /**
     * 锁Integer类型导致的死锁
     */
    private static Integer flagInt = Integer.valueOf(1);

    public void lockInteger() {
        synchronized (flagInt) {
            if (flagInt == 1) {
                this.run();
                flagInt = 2;
            }
        }
    }

    private void run() {
        String name = Thread.currentThread().getName();
        System.out.println(name + "【开始执行】");
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(name + "【执行结束】");
    }

}
